Maven Cook Book

Maven来自Apache,是一种软件项目管理工具,可以很便捷地管理软件项目的依赖处理、构建、文档化等过程,被广泛应用于各类软件项目。由于其出色的包依赖管理功能,许多Java项目都采用了Maven来进行项目管理。

Repository

通常,我们的仓库(Repository)来源是官方Maven仓库;有时为了更快的访问速度,使用其mirror作为来源;而有时需要搭建私有仓库,如由于网络环境限制无法直连到官方Maven仓库或其mirror,可以使用Nexus Repository Manager OSS等“私服”,这里不详细描述,侧重探讨在本地的使用。

安装

  • 由于Maven的运行需要JDK,因此先安装好JDK,并配置好$JAVA_HOME环境变量。
  • 从Maven的官方网站下载压缩包后解压。
  • 将Maven的解压路径添加到到$PATH环境变量中,从而可以全局使用。
  • 验证:mvn -v

概念

group

通常指一个大的项目组织,由groupId来标识。同时也是Maven管理的Java项目中最外层的统一包名,格式为com.[公司名/组织名].[项目名],e.g. com.ourcompany.greatprojectorg.springframework

artifact

通常指项目中的子项目/模块,表示大项目包含的小项目,或项目中独立的功能模块,由artifactId来标识。如果项目的规模不是很大,通常指的就是项目本身了。
以Spring框架的core模块为例:

1
2
3
4
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</dependency>

POM

Project Object Model,Maven的配置文件,以xml的形式组织。

配置

Maven配置由pom.xml文件来进行,由机器配置及项目具体配置共同作用。

机器配置

机器配置对Maven的通用行为进行定义,如本地仓库、远程服务器等,对机器上所有Maven管理的项目有效。

作用域
  • 全局配置,如果没有配置用户配置的话,对于机器上的所有用户有效,默认放在${maven.home}/conf/settings.xml下,设置新的全局配置:mvn -gs /path/to/global/settings.xml
  • 用户配置,对单个用户有效,默认放在${user.home}/.m2/settings.xml下,设置新的用户配置:mvn -s /path/to/user/settings.xml
标签

机器配置由<settings>标签包围,主要包含以下标签。

localRepository

本地仓库,存放artifacts,默认存放在${user.home}/.m2/repository

interactiveMode

交互模式,当需要的时候maven是否等待用户输入,默认为true。

offline

离线模式,当构建服务器完全无法连接远程仓库的时候使用,适用于完全本地化的Maven仓库。

pluginGroups

插件组的集合,包含一个个pluginGroup。

pluginGroup

插件group,用于加强maven的功能。

servers

用于保存连接到远程仓库的认证信息,如密码、密钥等。

mirrors

用于下载artifacts的远端仓库的镜像站点。

proxies

跟远程仓库连接的代理设置。

profiles

profile可以根据构建环境的不同为构建提供不同的参数、变量等动态内容,可以配置多个,但只有激活的profile才会生效。profile包含id, activation, properties, repositories以及pluginRepositories这四个属性。
profile的激活有三种方式:当满足activation中定义的所有条件时;当activeProfiles指定了profile的id时;当构建时使用了-P参数指定了profile的id时(e.g. mvn install -P prd)。

  • activation
    激活profile的方式之一,当其中的所有条件满足时,激活所在的profile,如下使当前的profile默认激活:

    1
    2
    3
    <activation>
    <activeByDefault>true</activeByDefault>
    </activation>

    也可以指定jdk,os,property等条件,当满足条件时,激活所在的profile,如下面的例子中,当jdk的版本的1.5时才激活。

    1
    2
    3
    4
    <activation>
    <activeByDefault>false</activeByDefault>
    <jdk>1.5</jdk>
    </activation>

    下面的例子,当mvn install -Dtest=true时激活该profile。

    1
    2
    3
    4
    5
    6
    7
       <activation>
    <activeByDefault>false</activeByDefault>
    <property>
    <name>test</name>
    <value>true</value>
    </property>
    </activation>

    下面的例子,当指定文件存在时激活该profile,判断存在使用<exists>,判断不存在使用<missing>

    1
    2
    3
    4
    5
    6
    7
       <activation>
    <activeByDefault>false</activeByDefault>
    <file>
    <exists>/usr/local/bin/x</exists>
    <missing>/usr/local/bin/y</missing>
    </file>
    </activation>
  • properties
    指定该profile的一些属性,如下例,当该profile被激活时,pkg.environment变量设置为prd。

    1
    2
    3
    <properties>
    <pkg.environment>prd</pkg.environment>
    </properties>

    properties可以在pom文件中被引用,有多种来源,如普通的变量(上例${pkg.environment}),env(e.g. ${env.PATH}显示OS中的$PATH环境变量),project(e.g. ${project.version}定义在<project><version>1.0</version></project>中)等等。

  • repositories
    指定该profile要连接的远程仓库的信息,用于解决包依赖关系,包含id,url,releases,snapshots等属性。
    releases和snapshots为针对artifact的策略,如只允许下载snapshots的artifact。

  • pluginRepositories
    指定该profile要连接的远程插件仓库的信息。与repositories的不同之处在于这个标签定义的是用到的maven插件所在的仓库,与依赖关系的解决没有关联。

activeProfiles

根据profile的ID指定激活的profile,这是让profile激活的另外一种方式。

下面是一个具体的例子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">

<localRepository>${user.home}/.m2/repository</localRepository>
<interactiveMode>true</interactiveMode>
<offline>false</offline>
<pluginGroups>
<pluginGroup>com.your.plugins</pluginGroup>
</pluginGroups>
<servers>
<server>
<id>nexus-releases</id>
<username>admin</username>
<password>admin123</password>
</server>
<server>
<id>nexus-snapshots</id>
<privateKey>/path/to/private/key</privateKey>
<passphrase>optional; leave empty if not used.</passphrase>
</server>
</servers>
<mirrors>
<mirror>
<id>mirrorId</id>
<mirrorOf>repositoryId</mirrorOf>
<name>Human Readable Name for this Mirror.</name>
<url>http://my.repository.com/repo/path</url>
</mirror>
</mirrors>
<profiles>
<profile>
<id>jdk-1.7</id>
<activation>
<activeByDefault>true</activeByDefault>
<jdk>1.7</jdk>
</activation>
<properties>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
<maven.compiler.compilerVersion>1.7</maven.compiler.compilerVersion>
</properties>
</profile>
<profile>
<id>default-profile</id>
<repositories>
<repository>
<id>central</id>
<url>http://10.20.10.30:8081/nexus/content/groups/public</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>repo-osc-thirdparty</id>
<url>http://10.20.10.30:8081/nexus/content/groups/public</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>central</id>
<url>http://10.20.10.30:8081/nexus/content/groups/public</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</profile>
</profiles>
<activeProfiles>
<activeProfile>default-profile</activeProfile>
</activeProfiles>
</settings>

项目配置

项目配置具体定义某个项目的Maven行为,重点解决依赖关系,位于项目的根目录下。项目中的变量可以在项目pom文件内直接引用,e.g. ${project.build.finalName}

标签

项目配置由<project>标签包围,主要包含以下标签。

parent

父项目的信息,可以根据父项目的groupId, artifactId及version唯一确定父项目,从而确立归属关系。使用了<parent>标签后,应免去<groupId>标签,直接从父项目继承groupId。

1
2
3
4
<parent>
<groupId>big-project</groupId>
<artifactId>my-project</artifactId>
</parent>

gourpId

标识大的项目,前文已经描述过,不再赘述。

artifactId

标识子项目名或模块名,不再赘述。

packaging

项目构建的目标类型,如pom,jar,war等。

version

项目的当前版本。

name

项目的名称,用于文档的生成。

url

项目主页的url,用于文档的生成。

modules

在父项目中,定义其下辖的子项目或模块,里面各个<module>标签的值为子项目或模块的artifactId。

properties

声明一些变量,可以在pom文件内引用。

dependencies

项目配置依赖关系的核心,定义了该项目所依赖的包。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.1.6.RELEASE</version>
<!-- 依赖类型, e.g. jar(默认), war, pom -->
<type>jar</type>
<!-- 依赖范围
compile, (默认)用于编译
provided, 与compile相似, 但表明项目在运行阶段期待由jdk或web容器来提供该依赖
runtime, 在项目运行时需要
test, 用于test任务
system, 与provided相似, 但需要通过systemPath指明jar的位置
systemPath, 与system配合
import, 只支持denpendencyManagement中的pom类型的依赖, 表明此依赖将被dependencyManagement中定义的pom类型的依赖替代
-->

<scope>compile</scope>
<!-- 分类器, 区分同一个pom文件中不同构建方式的组件, 如某个组件使用jdk1.6编译, 另一个使用jdk1.7编译;分类器名会附加到文件名的版本号后 -->
<classifier>jdk16</classifier>
</dependency>
</dependencies>

dependencyManagement

通常在父项目中定义,用于定义一系列可以给子项目或模块使用的依赖,子标签是dependencies及其中的一个个dependency。当子项目的dependency中指明了依赖的groupId和artifactId,且没有指明其他信息,依赖的其他信息从这里获取并填充。

distributionManagement

设置项目分发部署信息,即mvn deploy的目的地。对于私有Maven仓库,如Nexus,在此处进行集成。

1
2
3
4
5
6
7
8
9
10
11
12
<distributionManagement>
<repository>
<id>nexus-releases</id>
<name>Nexus Release Repository</name>
<url>http://10.20.20.20:8081/nexus/content/repositories/releases/</url>
</repository>
<snapshotRepository>
<id>nexus-releases</id>
<name>Nexus Snapshot Repository</name>
<url>http://10.20.20.20:8081/nexus/content/repositories/snapshots/</url>
</snapshotRepository>
</distributionManagement>

profile

作用与机器配置的profile类似(mvn install -P prd)。

build

配置项目的构建过程的具体信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
<build>
<!-- 以下除了pluginManagement外, 标签的值均为默认值 -->
<!-- 整个项目编译及打包的根目录 -->
<directory>${project.basedir}/target</directory>
<!-- 正式代码编译文件(.class)路径 -->
<outputDirectory>${project.build.directory}/classes</outputDirectory>
<!-- 项目打包后的名称 -->
<finalName>${project.artifactId}-${project.version}</finalName>
<!-- 测试代码的编译文件(.class)路径 -->
<testOutputDirectory>${project.build.directory}/test-classes</testOutputDirectory>
<!-- 正式代码路径 -->
<sourceDirectory>${project.basedir}/src/main/java</sourceDirectory>
<!-- 脚本路径 -->
<scriptSourceDirectory>src/main/scripts</scriptSourceDirectory>
<!-- 测试代码路径 -->
<testSourceDirectory>${project.basedir}/src/test/java</testSourceDirectory>
<!-- 项目的resource目录 -->
<resources>
<resource>
<directory>${project.basedir}/src/main/resources</directory>
</resource>
</resources>
<!-- 测试代码所用的resource目录 -->
<testResources>
<testResource>
<directory>${project.basedir}/src/test/resources</directory>
</testResource>
</testResources>
<!-- 插件管理, 与dependencyManagement类似, 也通常是在父项目中定义pluginManagement,包含plugins及其中的plugin,在子项目或模块中定义单独的plugins, 子项目或模块中plugins的plugin标签内不全的信息从父项目的pluginManagement中补全 -->
<pluginManagement>
<!-- 这里举几个常用插件作为例子 -->
<plugins>
<!-- Maven jar插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.6</version>
<configuration>
<archive>
<addMavenDescriptor>true</addMavenDescriptor>
<index>true</index>
<manifest>
<addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
<addDefaultImplementationEntries>true</addDefaultImplementationentries>
</manifest>
</archive>
</configuration>
</plugin>
<!-- Maven war插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.1.1</version>
<configuration>
<webXml>src/main/webapp/WEB-INF/web.xml</webXml>
<warSourceDirectory>src/main/webapp</warSourceDirectory>
<archive>
<addMavenDescriptor>false</addMavenDescriptor>
</archive>
<warName>${project.build.finalName}</warName>
<webResources>
<resource>
<!-- 还记得前文的pkg.environment变量吗? -->
<directory>src/main/resources/config/${pkg.environment}</directory>
<targetPath>WEB-INF/classes</targetPath>
<filtering>true</filtering>
</resource>
</webResources>
<warSourceExcludes>src/main/resources/config</warSourceExecludes>
</configuration>
</plugin>
<!-- Maven install插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<!-- Maven source插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>2.4</version>
<executions>
<execution>
<id>attach-sources</id>
<phase>package</phase>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- Maven deploy插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
<!-- Maven compiler插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
<encoding>UTF-8</encoding>
<!-- 以下常用于war包项目 -->
<compilerArguments>
<extdirs>src\main\webapp\WEB-INF\lib</extdirs>
</compilerArguments>
</configuration>
</plugin>
<!-- Maven surefire插件, 用于测试 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.19</version>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
<!-- tomcat插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<port>8080</port>
<path>/</path>
</configuration>
<!-- 插件同样有依赖关系 -->
<dependencies>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>19.0</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</pluginManagement>
</build>

操作

生命周期

Maven在处理项目时,具有三个独立的生命周期,每个周期有若干个顺序执行的阶段。

Clean生命周期

Clean生命周期的任务主要是清理上次构建的结果文件,包含以下阶段:

  1. pre-clean
    Clean的一些准备工作。
  2. clean
    进行Clean,移除上次构建生成的文件。
  3. post-clean
    Clean的一些善后工作。
    Default生命周期
    在Default生命周期,Maven对项目进行构建,包含以下阶段:
  4. validate
    验证项目正常。
  5. generate-sources
    生成编译阶段所需的代码。
  6. process-sources
    处理上一步产生的代码。
  7. generate-resources
    生成resource文件。
  8. process-resources
    复制并处理resource文件到目标目录。
  9. compile
    编译项目源码。
  10. process-classes
    处理编译后的文件。
  11. generate-test-sources
    生成测试代码。
  12. process-test-sources
    处理测试代码。
  13. generate-test-resources
    生成测试用resource文件。
  14. process-test-resources
    复制并处理资源文件到目标测试目录。
  15. test-compile
    编译测试源代码。
  16. process-test-classes
    处理编译后的测试文件。
  17. test
    使用单元测试框架对编译后的源码进行测试,测试代码不会被打包或部署。
  18. prepare-package
    打包前的准备工作。
  19. package
    将编译后的源码打包成待发布的格式,如jar。
  20. pre-integration-test
    集成测试的准备工作。
  21. integration-test
    将包处理并部署到集成测试环境。
  22. post-integration-test
    集成测试的善后工作。
  23. verify
    验证打包的文件符合质量条件。
  24. install
    将打包的结果保存到本地仓库,此时包可以作为其他项目的dependency。
  25. deploy
    发布到远程仓库。

虽然Default生命周期包含这么多阶段,但需要我们关注的阶段只有validate, compile, test, package, verify, install以及deploy。

Site生命周期

Site生命周期主要生成站点文档及相关的部署,包含以下阶段:

  1. pre-site
    执行一些需要在站点文档生成前完成的任务。
  2. site
    生成项目的站点文档。
  3. post-site
    执行一些需要在站点文档生成后完成的任务,并做一些站点部署的准备工作。
  4. site-deploy
    将站点文档部署到指定的服务器上。

命令

所有Maven的命令均由mvn开头,用以控制Maven的生命周期,我们常用的命令主要是mvn clean, mvn validate, mvn compile, mvn test, mvn package, mvn verify, mvn install, mvn deploy以及mvn site
命令的使用有以下几个规则:

  1. 可以组合命令,e.g. mvn clean install,会同时执行clean及install任务。
  2. 在某个生命周期中,执行某阶段的命令,会自动将其前面阶段的命令都执行一次。如mvn clean install会等同于mvn pre-clean clean validate compile test package verify install(部分处理阶段省略)。
  3. 命令的参数如果是缩写(e.g. -D),可以省略参数名与参数值中间的空格(e.g. -DskipTests=true);如果不是缩写,则必须加空格(e.g. –define skipTests=true)。
参数

执行Maven命令时可以带上若干个参数。

信息
  • 显示帮助信息。

    1
    2
    mvn -h
    mvn --help
  • 显示版本信息。

    1
    2
    mvn -v
    mvn --version
配置文件
  • 指定全局配置文件。

    1
    2
    mvn -gs <全局配置文件>
    mvn --global-settings <全局配置文件>
  • 指定用户配置文件。

    1
    2
    mvn -s <用户配置文件>
    mvn --settings <用户配置文件>
属性参数
  • 添加属性。
    1
    2
    3
    mvn -D argName=argValue
    mvn --define argName=argValue
    mvn -DargName=argValue
profile
  • 指定激活的profile。
    1
    2
    mvn -Pprofile1,profile2
    mvn --active-profiles profile1,profile2
日志
  • 只显示错误信息。

    1
    2
    mvn -q
    mvn --quiet
  • 显示执行错误信息,打印完整的stack trace

    1
    2
    mvn -e
    mvn --errors
  • 显示调试信息。

    1
    2
    mvn -X
    mvn --debug
下载
  • 强制从远程仓库更新本项目所有依赖的snapshot,保持依赖处于最新状态。
    1
    2
    mvn -U
    mvn --update-snapshots

参考资料

Apache Maven Official Site - Apache Maven
POM Reference - Apache Maven
Introducation to the Build Lifecycle - Apache Maven